package com.philosophy.web.listener;

import com.google.common.hash.BloomFilter;
import com.philosophy.constant.RedisConstant;
import com.philosophy.web.domain.generate.RoutingDB;
import com.philosophy.web.domain.generate.UrlMapping;
import com.philosophy.web.event.LoadEvent;
import com.philosophy.web.filter.RedirectFilter;
import com.philosophy.web.mapper.UrlMappingMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Objects;

/**
 * @author philosophy
 */
@Component
@Slf4j
public class LoadDataListener {

    @Resource
    private UrlMappingMapper urlMappingMapper;

    @Resource
    private RedisTemplate<String, String> redisTemplate;


    /**
     * 加载数据到布隆过滤器和Redis
     */
    @EventListener
    @Async("taskPool")
    public void loadData(LoadEvent loadEvent) {
        log.info("=========开始加载数据");
        long total = urlMappingMapper.count();

        long n = total / 1000;

        BloomFilter<String> bloomFilter = RedirectFilter.createBloomFilter(total / 2 * 3);
        RedirectFilter redirectFilter = (RedirectFilter) loadEvent.getSource();
        //每次读取一千条数据
        for (int i = 0; i <= n; i++) {
            List<UrlMapping> urlMappings = urlMappingMapper.findOffset(1000L * i, 1000);
            //加载数据到布隆过滤器
            RedirectFilter.loadBloomFilter(urlMappings, bloomFilter);
            //加载数据到Redis
            loadRedis(urlMappings);
        }

        //一次性替换布隆过滤器  copyOnWrite
        Field field = ReflectionUtils.findField(RedirectFilter.class, "bloomFilter");
        if (Objects.nonNull(field)) {
            ReflectionUtils.makeAccessible(field);
            ReflectionUtils.setField(field, redirectFilter, bloomFilter);
        }
        log.info("=========加载数据完成!");
    }

    /**
     * 加载数据到redis
     *
     * @param urlMappings urlMappings
     */
    private void loadRedis(List<UrlMapping> urlMappings) {
        for (UrlMapping urlMapping : urlMappings) {
            String shortUrl = urlMapping.getShortUrl();
            int databaseNum = RoutingDB.getDatabaseNum(shortUrl);
            String key = String.format(RedisConstant.COUNT_KEY_FORMAT, databaseNum);
            redisTemplate.opsForHash().put(key, shortUrl, urlMapping);
        }
    }


}
